package com.sven.idrivingfutureserver.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.sven.idrivingfutureserver.common.CommonCode;
import com.sven.idrivingfutureserver.common.OptCode;
import com.sven.idrivingfutureserver.common.Response;
import com.sven.idrivingfutureserver.common.RestResponse;
import com.sven.idrivingfutureserver.exception.RestException;
import com.sven.idrivingfutureserver.mapper.UserMapper;
import com.sven.idrivingfutureserver.pojo.*;
import com.sven.idrivingfutureserver.service.LoginService;
import com.sven.idrivingfutureserver.util.AliPhoneUtil;
import com.sven.idrivingfutureserver.util.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * @ClassName LoginServiceImpl
 * @Description
 * @Author 小张
 * @Date 2024/5/29 11:06
 * @Version 1.0
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LoginServiceImpl implements LoginService {

    private final WxMaService wxMaService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private AliPhoneUtil aliPhoneUtil;

    private static final long SEND_SMS_TIME_INTERVAL = 60 * 5L;   // 发送短信的时间间隔
    private static final long MAKE_PHONE_CALL_TIME_INTERVAL = 60 * 5L;   // 打电话的时间间隔

    // 二维码图片oss地址前缀
    private static final String OSS_URL_PREFIX = "https://xiaoer-nuoche.oss-cn-chengdu.aliyuncs.com/qrcode/";

    @Override
    public Response wechatLogin(String code) {
        if (StringUtils.isEmpty(code)) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        try {
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            String openId = sessionInfo.getOpenid();
            String unionId = sessionInfo.getUnionid();
            String sessionKey = sessionInfo.getSessionKey();
            // 自己的业务
            UserInfo userInfoFetched = userMapper.getUserByOpenId(openId);
            // 构造返回数据
            HashMap<String, Object> respData = new HashMap<>();
            UserInfo userInfo = new UserInfo();
            userInfo.setOpenId(openId);
            userInfo.setUnionId(unionId);
            userInfo.setSessionKey(sessionKey);
            userInfo.setLastLogin(DateUtil.getTime());
            // 已注册
            if (null != userInfoFetched) {
                userMapper.updateLastLogin(userInfo);
                userInfoFetched.setSessionKey(null);
                if (!StringUtils.isEmpty(userInfo.getUsername())) {
                    respData.put("userInfo", userInfoFetched);
                }
                StpUtil.login(userInfoFetched.getId(), "miniApp");
            } else {
                // 未注册
                userInfo.setRegTime(DateUtil.getTime());
                userMapper.saveUser(userInfo);
                StpUtil.login(userInfo.getId(), "miniApp");
            }
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            respData.put("tokenName", tokenInfo.getTokenName());
            respData.put("tokenValue", tokenInfo.getTokenValue());
            respData.put("ossUrlPrefix", OSS_URL_PREFIX);
            return new RestResponse<>(CommonCode.SUCCESS, respData);
        } catch (WxErrorException e) {
            log.error(e.getMessage(), e);
            throw new RestException(CommonCode.WX_LOGIN_ERROR, null);
        } finally {
            WxMaConfigHolder.remove(); //清理ThreadLocal
        }
    }

    @Override
    public Response postUserInfo(UserInfo userInfo) {
        userInfo.setId(StpUtil.getLoginIdAsInt());
        userMapper.postUserInfo(userInfo);
        return new Response(CommonCode.SUCCESS);
    }

    @Override
    public Response isQrCodeBinging(String qrcodeStr) {
        Qrcode qrcode = userMapper.getQrcode(qrcodeStr);
        if (null == qrcode) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        Map<String, Object> resp = new HashMap<>();
        // 没有绑定
        if (qrcode.getIsBinding() == 0) {
            resp.put("isBinding", false);
            return new RestResponse<>(CommonCode.SUCCESS, resp);
        }
        // 绑定了，返回车主信息
        resp.put("isBinding", true);
        UserInfo userInfo = userMapper.getUserById(qrcode.getUserId());
        // 屏蔽敏感信息
        userInfo.setOpenId(null);
        userInfo.setUnionId(null);
        userInfo.setSessionKey(null);
        resp.put("userInfo", userInfo);
        return new RestResponse<>(CommonCode.SUCCESS, resp);
    }

    /**
     * 绑定挪车码与手机号
     */
    @Transactional
    @Override
    public Response bindPhoneNumber(String code, String qrcode) {
        Qrcode qrcodeInfo = userMapper.getQrcode(qrcode);
        if (null == qrcodeInfo) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        if (qrcodeInfo.getIsBinding() == 1) {
            throw new RestException(CommonCode.QRCODE_ALREADY_BINGING, null);
        }
        try {
            // 首先用code去微信换取手机号
            WxMaPhoneNumberInfo wxPhone = wxMaService.getUserService().getPhoneNoInfo(code);
            PhoneNumber phoneNumber = new PhoneNumber();
            phoneNumber.setUserId(StpUtil.getLoginIdAsInt());
            phoneNumber.setPhoneNumber(wxPhone.getPhoneNumber());
            phoneNumber.setPurePhoneNumber(wxPhone.getPurePhoneNumber());
            phoneNumber.setCountryCode(wxPhone.getCountryCode());
            phoneNumber.setIsMainNumber(1); // 默认绑定的号码就是主号
            // 检查用户是否已经有配置了
            SettingDetail settingDetail = userMapper.getMySettingDetail(StpUtil.getLoginIdAsInt());
            if (null == settingDetail) {
                // 存储用户手机号
                userMapper.savePhoneNumber(phoneNumber);
                // 给用户默认设置
                UserSetting setting = getUserSetting(phoneNumber);
                userMapper.saveUserSetting(setting);
            } else {
                // 是绑定多码的情况
                ArrayList<PhoneNumber> phoneNumbers = settingDetail.getPhoneNumbers();
                boolean isNewNumber = true; // 是否是新号码
                for (PhoneNumber number : phoneNumbers) {
                    if (StringUtils.equals(number.getPhoneNumber(), phoneNumber.getPhoneNumber())) {
                        isNewNumber = false;
                        break;
                    }
                }
                if (isNewNumber) {
                    // 存储用户手机号
                    userMapper.savePhoneNumber(phoneNumber);
                }
            }
            // 更新二维码绑定信息
            qrcodeInfo.setIsBinding(1);
            qrcodeInfo.setUserId(StpUtil.getLoginIdAsInt());
            qrcodeInfo.setBindingTime(DateUtil.getTime());
            userMapper.updateQrcodeByQrStr(qrcodeInfo);
            // 记录日志
            saveOptLog(OptCode.BINDING, null);
            return new Response(CommonCode.SUCCESS);
        } catch (WxErrorException e) {
            log.error(e.getMessage());
            throw new RestException(CommonCode.GET_PHONE_NUMBER_ERROR, null);
        } finally {
            WxMaConfigHolder.remove(); //清理ThreadLocal
        }
    }

    @Override
    public Response getMyQrCodes() {
        List<Qrcode> myQrcodeList = userMapper.getMyQrcodeList(StpUtil.getLoginIdAsInt());
        return new RestResponse<>(CommonCode.SUCCESS, myQrcodeList);
    }

    /**
     * 保存操作日志
     *
     * @param opt          操作值
     * @param targetUserId 目标用户id
     */
    public void saveOptLog(OptCode opt, Integer targetUserId) {
        OptLog optLog = new OptLog();
        optLog.setOptTime(DateUtil.getTime());
        optLog.setOpt(opt.code());
        optLog.setOptUserId(StpUtil.getLoginIdAsInt());
        optLog.setTargetUserId(targetUserId);
        userMapper.saveOptLog(optLog);
    }

    // 生成用户默认设置
    private static UserSetting getUserSetting(PhoneNumber phoneNumber) {
        UserSetting setting = new UserSetting();
        setting.setUserId(phoneNumber.getUserId());
        setting.setPrivacySwitch(1);
        setting.setWechatSwitch(1);
        setting.setSmsSwitch(1);
        setting.setNoDisturb(0);
        setting.setNoDisturbStartTime(22);
        setting.setNoDisturbEndTime(7);
        setting.setLeaveMsg("临时停车，请多关照");
        setting.setOptTime(DateUtil.getTime());
        return setting;
    }

    @Override
    public Response getMySettingInfo(String qrcode) {
        if (StringUtils.isEmpty(qrcode)) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        SettingDetail setting = userMapper.getMySettingDetail(StpUtil.getLoginIdAsInt());
        if (null == setting) {
            throw new RestException(CommonCode.USER_SETTING_NOT_EXIST, null);
        }
        processSettingToMatchQrcode(setting, qrcode);
        return new RestResponse<>(CommonCode.SUCCESS, setting);
    }

    @Override
    public Response updateMySettingInfo(SettingDetail settingDetail) {
        if (null == settingDetail) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        settingDetail.setUserId(StpUtil.getLoginIdAsInt());
        settingDetail.setOptTime(DateUtil.getTime());
        userMapper.updateMySettingInfo(settingDetail);
        SettingDetail detail = userMapper.getMySettingDetail(StpUtil.getLoginIdAsInt());
        return new RestResponse<>(CommonCode.SUCCESS, detail);
    }

    @Override
    public Response generateQrcodeList(Integer num, String pwd) {
        if (!StringUtils.equals(pwd, "kKxbEe3cD632PkbG")) {
            return new Response(CommonCode.FAIL);
        }
        if (num <= 0 || num >= 5000) {
            throw new RestException(CommonCode.QRCODE_GEN_NUM_ERROR, num);
        }
        ArrayList<Qrcode> qrcodeList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Qrcode qrcode = new Qrcode();
            qrcode.setQrcode(UUID.randomUUID().toString().replace("-", ""));
            qrcode.setCreateTime(DateUtil.getTime());
            qrcodeList.add(qrcode);
        }
        int count = userMapper.batchCachingQrcodeList(qrcodeList);
        return new RestResponse<>(CommonCode.SUCCESS, count);
    }

    @Override
    public Response getUserInfoByPlateNumber(String plateNumber) {
        if (StringUtils.isEmpty(plateNumber) || plateNumber.length() <= 3) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        SettingDetail setting = userMapper.getUserSettingByPlateNumber(plateNumber.toUpperCase());
        HashMap<String, Object> resMap = new HashMap<>();
        if (null == setting) {
            resMap.put("isExist", false);   // 车牌号信息不存在
            return new RestResponse<>(CommonCode.SUCCESS, resMap);
        }
        ArrayList<Qrcode> qrcodeList = setting.getQrcodes();
        if (null == qrcodeList || qrcodeList.isEmpty()) {
            resMap.put("isExist", false);   // 车牌号信息不存在
            return new RestResponse<>(CommonCode.SUCCESS, resMap);
        }
        resMap.put("isExist", true);
        if (setting.getPrivacySwitch() == 1) {
            setting.setPhoneNumbers(null);
        }
        setting.setPlateNumbers(null);
        resMap.put("setting", setting);
        return new RestResponse<>(CommonCode.SUCCESS, resMap);
    }

    @Transactional
    @Override
    public Response unBindQrCode(String qrcode) {
        if (StringUtils.isEmpty(qrcode)) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        if (qr.getIsBinding() == 1 && qr.getUserId() != StpUtil.getLoginIdAsInt()) {
            throw new RestException(CommonCode.QRCODE_ALREADY_BINGING_BY_OTHERS, null);
        }
        Integer plateNumberId = qr.getPlateNumberId();
        if (null != plateNumberId && plateNumberId > 0) {
            userMapper.removePLateNumber(plateNumberId);    // 移除已解绑的车牌号
        }
        qr.setIsBinding(0);
        qr.setUserId(null);
        qr.setBindingTime(null);
        qr.setPlateNumberId(null);
        userMapper.updateQrcodeByQrStr(qr);
        saveOptLog(OptCode.UNBINDING, null);
        return new Response(CommonCode.SUCCESS);
    }

    @Transactional
    @Override
    public Response bindQrCode(String qrcode, Integer plateNumberId) {
        if (StringUtils.isEmpty(qrcode)) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        if (qr.getIsBinding() == 1) {
            throw new RestException(CommonCode.QRCODE_ALREADY_BINGING, null);
        }
        if (null == plateNumberId || plateNumberId <=0) {
            plateNumberId = null;
        }
        qr.setIsBinding(1);
        qr.setUserId(StpUtil.getLoginIdAsInt());
        qr.setBindingTime(DateUtil.getTime());
        qr.setPlateNumberId(plateNumberId);
        userMapper.updateQrcodeByQrStr(qr);
        saveOptLog(OptCode.BINDING, null);
        return new RestResponse<>(CommonCode.SUCCESS, qr);
    }

    @Override
    public Response changePhoneNumber(String code, Integer phoneNumberId) {
        PhoneNumber phoneNumber = userMapper.getPhoneNumberById(phoneNumberId);
        if (null == phoneNumber) {
            throw new RestException(CommonCode.PHONE_NUMBER_NOT_EXIST, null);
        }
        try {
            // 首先用code去微信换取手机号
            WxMaPhoneNumberInfo wxPhone = wxMaService.getUserService().getPhoneNoInfo(code);
            phoneNumber.setUserId(StpUtil.getLoginIdAsInt());
            phoneNumber.setPhoneNumber(wxPhone.getPhoneNumber());
            phoneNumber.setPurePhoneNumber(wxPhone.getPurePhoneNumber());
            phoneNumber.setCountryCode(wxPhone.getCountryCode());
            phoneNumber.setId(phoneNumberId);
            userMapper.updateUserPhoneNumber(phoneNumber);
            saveOptLog(OptCode.CHANGE_PHONE_NUMBER, null);
            return new RestResponse<>(CommonCode.SUCCESS, phoneNumber);
        } catch (WxErrorException e) {
            log.error(e.getMessage());
            throw new RestException(CommonCode.GET_PHONE_NUMBER_ERROR, null);
        } finally {
            WxMaConfigHolder.remove(); //清理ThreadLocal
        }
    }

    @Override
    @Transactional
    public Response changePlateNumber(String plateNumber, String qrcode) {
        if (StringUtils.isEmpty(plateNumber) || plateNumber.length() <= 3) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.PLATE_NUMBER_NOT_EXIST, null);
        }
        if (qr.getUserId() != StpUtil.getLoginIdAsInt()) {
            throw new RestException(CommonCode.NO_PERMISSION, null);
        }
        plateNumber = plateNumber.toUpperCase();
        PlateNumber pln = new PlateNumber();
        if (null == qr.getPlateNumberId()) {
            // 新添加车牌号
            pln.setPlateNumber(plateNumber);
            pln.setUserId(StpUtil.getLoginIdAsInt());
            userMapper.savePlateNumber(pln);
            qr.setPlateNumberId(pln.getId());
            qr.setIsBinding(1);
            qr.setBindingTime(DateUtil.getTime());
            userMapper.updateQrcodeByQrStr(qr);
            saveOptLog(OptCode.ADD_PLATE_NUMBER, null);
        } else {
            // 修改车牌号
            pln.setId(qr.getPlateNumberId());
            pln.setPlateNumber(plateNumber);
            pln.setUserId(StpUtil.getLoginIdAsInt());
            userMapper.updatePlateNumber(pln);
            saveOptLog(OptCode.CHANGE_PLATE_NUMBER, null);
        }
        return new RestResponse<>(CommonCode.SUCCESS, pln);
    }

    @Override
    public Response getUserInfoByQrCode(String qrcode) {
        Qrcode qrPojo = userMapper.getQrcode(qrcode);
        if (null == qrPojo) {
            throw new RestException(CommonCode.QRCODE_NOT_EXIST, null);
        }
        if (qrPojo.getIsBinding() != 1 || null == qrPojo.getUserId()) {
            log.error("getUserInfoByQrCode error: qrcode = {}", qrcode);
            throw new RestException(CommonCode.QRCODE_NOT_BINDING, null);
        }
        SettingDetail setting = userMapper.getMySettingDetail(qrPojo.getUserId());
        processSettingToMatchQrcode(setting, qrcode);
        // 开启隐私则隐藏号码
        if (setting.getPrivacySwitch() == 1) {
            setting.setPhoneNumbers(null);
        }
        saveOptLog(OptCode.SCAN_QRCODE, qrPojo.getUserId());
        return new RestResponse<>(CommonCode.SUCCESS, setting);
    }

    /**
     * 处理用户的设置信息，只保留当前qrcode对应的车牌信息
     * @param setting 设置详情
     * @param qrcode  挪车码值
     */
    private void processSettingToMatchQrcode(SettingDetail setting, String qrcode) {
        ArrayList<PlateNumber> plateNumbers = setting.getPlateNumbers();
        ArrayList<Qrcode> qrcodeList = setting.getQrcodes();
        ArrayList<Qrcode> qList = new ArrayList<>();
        ArrayList<PlateNumber> pList = new ArrayList<>();
        for (Qrcode q : qrcodeList) {
            if (StringUtils.equals(qrcode, q.getQrcode())) {
                qList.add(q);
                for (PlateNumber p : plateNumbers) {
                    if (null != q.getPlateNumberId() && q.getPlateNumberId() == p.getId()) {
                        pList.add(p);
                        break;
                    }
                }
                break;
            }
        }
        setting.setPlateNumbers(pList);
        setting.setQrcodes(qList);
    }

    /**
     * 获取用户的隐私号码
     * @param qrcode 用户挪车码
     */
    @Override
    public Response getPrivacyNumber(String qrcode) {
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.USER_NOT_EXIST, null);
        }
        ArrayList<PhoneNumber> phoneNumbers = userMapper.getPhoneNumberListByUserId(qr.getUserId());
        if (null == phoneNumbers || phoneNumbers.isEmpty()) {
            throw new RestException(CommonCode.PHONE_NUMBER_NOT_EXIST, null);
        }
        OptLog optLog = userMapper.getOptLogByCodeAndUserId(OptCode.CALLING.code(), StpUtil.getLoginIdAsInt());
        if (null != optLog) {
            String optTime = optLog.getOptTime();
            long secondsDiff = DateUtil.getSecondsDiffOf2TimeStr(optTime, DateUtil.getTime());
            if (secondsDiff < MAKE_PHONE_CALL_TIME_INTERVAL) {
                throw new RestException(CommonCode.MAKE_PHONE_CALL_TOO_FAST, null);
            }
        }
        log.info("phoneNumbers: {}", phoneNumbers);
        String mainPhoneNumber = findMainPhoneNumber(phoneNumbers);
        //TODO 获取隐私号方法待实现
        // String phoneX = aliPhoneUtil.getPhoneX();
        // boolean success = aliPhoneUtil.bindAxN(mainPhoneNumber, phoneX);
        // saveOptLog(OptCode.CALLING, qr.getUserId());
        // HashMap<String, Object> resMap = new HashMap<>();
        // if (success) {
        //     resMap.put("success", true);    // 设置隐私号成功
        //     resMap.put("phoneNumber", phoneX);
        // } else {
        //     resMap.put("success", false);
        //     resMap.put("tipMsg", "暂无空闲隐私号可用，请稍后再试");
        // }

        HashMap<String, Object> resMap = new HashMap<>();
        resMap.put("success", true);    // 设置隐私号成功
        resMap.put("phoneNumber", mainPhoneNumber);
        return new RestResponse<>(CommonCode.SUCCESS, resMap);
    }

    @Override
    public Response sendSmsToMoveCar(String qrcode, String plateNumber) {
        Qrcode qr = userMapper.getQrcode(qrcode);
        if (null == qr) {
            throw new RestException(CommonCode.INVALID_PARAM, null);
        }
        SettingDetail settingDetail = userMapper.getMySettingDetail(qr.getUserId());
        ArrayList<PhoneNumber> phoneNumbers = settingDetail.getPhoneNumbers();
        if (null == phoneNumbers || phoneNumbers.isEmpty()) {
            throw new RestException(CommonCode.PHONE_NUMBER_NOT_EXIST, null);
        }
        OptLog log = userMapper.getOptLogByCodeAndUserId(OptCode.SMS.code(), StpUtil.getLoginIdAsInt());
        if (null != log) {
            String optTime = log.getOptTime();
            long secondsDiff = DateUtil.getSecondsDiffOf2TimeStr(optTime, DateUtil.getTime());
            if (secondsDiff < SEND_SMS_TIME_INTERVAL) {
                throw new RestException(CommonCode.SEND_SMS_TOO_FAST, null);
            }
        }
        boolean success;
        if (settingDetail.getSmsSwitch() == 0) {
            throw new RestException(CommonCode.SEND_SMS_NOT_ALLOWED, null);
        }
        String mainPhoneNumber = findMainPhoneNumber(phoneNumbers);
        if (StringUtils.isEmpty(plateNumber)) {
            success = aliPhoneUtil.sendSms(mainPhoneNumber);
        } else {
            success = aliPhoneUtil.sendSms(mainPhoneNumber, plateNumber);
        }
        if (success) {
            saveOptLog(OptCode.SMS, qr.getUserId());
        }
        HashMap<String, Object> resp = new HashMap<>();
        resp.put("success", success);
        resp.put("msg", success ? "短信发送成功" : "短信发送失败");
        return new RestResponse<>(CommonCode.SUCCESS, resp);
    }

    @Override
    public Response getScanRecords() {
        List<OptLogDetail> logs = userMapper.getScanRecords(StpUtil.getLoginIdAsInt());
        for (OptLogDetail logDetail : logs) {
            logDetail.setDesc(OptCode.getDescByCode(logDetail.getOpt()));
        }
        return new RestResponse<>(CommonCode.SUCCESS, logs);
    }

    private String findMainPhoneNumber(ArrayList<PhoneNumber> phoneNumbers) {
        String mainPhoneNumber = "";
        // 有多个号码时，找主号码
        if (null != phoneNumbers && !phoneNumbers.isEmpty()) {
            for (PhoneNumber phoneNumber : phoneNumbers) {
                if (phoneNumber.getIsMainNumber() == 1) {
                    mainPhoneNumber = phoneNumber.getPhoneNumber();
                    break;
                }
            }
            // 没找到主号码时，默认选第一个号码
            if (StringUtils.isEmpty(mainPhoneNumber)) {
                mainPhoneNumber = phoneNumbers.get(0).getPhoneNumber();
            }
        }
        return mainPhoneNumber;
    }
}
